package cn.xy.base.server.aspect;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import cn.xy.base.server.po.SysLog;
import cn.xy.base.server.service.SysLogService;
import cn.xy.base.server.vo.LoginResult;
import cn.xy.base.server.web.AdminWebContext;
import cn.xy.commons.annotation.OperateLog;
import cn.xy.commons.constant.Consts;
import cn.xy.commons.core.Result;
import cn.xy.commons.web.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 操作日志切面
 *
 *  @author xy
 */
@Slf4j
@Aspect
@Component
public class OperateLogAspect {
    private final ThreadLocal<SysLog> logLocal = new ThreadLocal<>();

    @Autowired
    private SysLogService sysLogService;

    @Pointcut("execution(public * *..*.admapi..*.*(..))")
    public void sysLog() {
        // empty function
    }

    @Before("sysLog()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        String path = request.getRequestURI();
        String ip = RequestUtil.getRemoteAddr(request);
        String query = JSONUtil.toJsonStr(request.getParameterMap());
        String data = JSONUtil.toJsonStr(joinPoint.getArgs());

        String methodName = joinPoint.getSignature().getName();
        Method method = currentMethod(joinPoint, methodName);
        if (method == null) {
            return;
        }
        OperateLog operateLog = method.getAnnotation(OperateLog.class);
        if (operateLog != null) {
            SysLog sysLog = new SysLog();
            sysLog.setUserId(AdminWebContext.getAdminId());
            sysLog.setUserName(AdminWebContext.getUsername());
            sysLog.setModule(operateLog.module());
            sysLog.setDescription(operateLog.description());
            sysLog.setContent(String.format("uri:[%s], query:[%s], data:[%s]",
                    path, operateLog.ignoreParam() ? "" : query, operateLog.ignoreData() ? "" : data));
            sysLog.setIp(ip);
            sysLog.setCreateTime(DateUtil.date());
            logLocal.set(sysLog);
        }
    }

    @AfterReturning(returning = "ret", pointcut = "sysLog()")
    public void doAfterReturning(Object ret) {
        SysLog sysLog = logLocal.get();
        if (sysLog != null && ret instanceof Result) {
            Result<?> result = (Result<?>) ret;
            if (result.getCode() == Consts.SUCCESS_CODE) {
                //操作成功
                Object data = result.getData();
                if (data instanceof LoginResult) {
                    sysLog.setUserId(AdminWebContext.getAdminId());
                    sysLog.setUserName(AdminWebContext.getUsername());
                }
                try {
                    sysLogService.save(sysLog);
                } catch (Exception e) {
                    log.error("syslog save fail: ", e);
                }
            }
        }
        logLocal.remove();
    }

    private Method currentMethod(JoinPoint joinPoint, String methodName) {
        Method[] methods = joinPoint.getTarget().getClass().getMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }
}
